//
//  UICarouselView.m
//  mobile
//
//  Created by Jocer on 2018/4/21.
//  Copyright © 2018年 azazie. All rights reserved.
//

#import "UICarouselView.h"
#import "TXFire.h"
#import "UIImageView+Loading.h"
#import "UIPageControl+LB.h"

#import <ImageIO/ImageIO.h>
#import <QMUIKit/QMUIKit.h>

#define DEFAULTTIME 5
#define HORMARGIN 10
#define VERMARGIN 5
#define DES_LABEL_H 20

float durationWithSourceAtIndex(CGImageSourceRef source, NSUInteger index) {
    float duration = 0.1f;
    CFDictionaryRef propertiesRef = CGImageSourceCopyPropertiesAtIndex(source, index, nil);
    NSDictionary *properties = (__bridge NSDictionary *)propertiesRef;
    NSDictionary *gifProperties = properties[(NSString *)kCGImagePropertyGIFDictionary];
    NSNumber *delayTime = gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime];
    if (delayTime) duration = delayTime.floatValue;
    else {
        delayTime = gifProperties[(NSString *)kCGImagePropertyGIFDelayTime];
        if (delayTime) duration = delayTime.floatValue;
    }
    CFRelease(propertiesRef);
    return duration;
}

UIImage *getImageWithData(NSData *data) {
    CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
    size_t count = CGImageSourceGetCount(imageSource);
    if (count <= 1) { //非gif
        CFRelease(imageSource);
        return [[UIImage alloc] initWithData:data];
    } else { //gif图片
        NSMutableArray *images = [NSMutableArray array];
        NSTimeInterval duration = 0;
        for (size_t i = 0; i < count; i++) {
            CGImageRef image = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
            if (!image) continue;
            duration += durationWithSourceAtIndex(imageSource, i);
            [images addObject:[UIImage imageWithCGImage:image]];
            CGImageRelease(image);
        }
        if (!duration) duration = 0.1 * count;
        CFRelease(imageSource);
        return [UIImage animatedImageWithImages:images duration:duration];
    }
}

UIImage *gifImageNamed(NSString *imageName) {
    
    if (![imageName hasSuffix:@".gif"]) {
        imageName = [imageName stringByAppendingString:@".gif"];
    }
    
    NSString *imagePath = [[NSBundle bundleForClass:[UICarouselView class]] pathForResource:imageName ofType:nil];
    NSData *data = [NSData dataWithContentsOfFile:imagePath];
    if (data) return getImageWithData(data);
    
    return [UIImage imageNamed:imageName];
}

@interface NSTimer (XRTimer)
+ (NSTimer *)__timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *))block;
@end


@implementation NSTimer (XRTimer)

+ (NSTimer *)__timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *))block {
    if ([self respondsToSelector:@selector(timerWithTimeInterval:repeats:block:)]) {
        return [self timerWithTimeInterval:interval repeats:repeats block:block];
    }
    return [self timerWithTimeInterval:interval target:self selector:@selector(timerAction:) userInfo:block repeats:repeats];
}

+ (void)timerAction:(NSTimer *)timer {
    void (^block)(NSTimer *timer) = timer.userInfo;
    if (block) block(timer);
}
@end

@interface UICarouselView() <UIScrollViewDelegate>

@property (nonatomic, strong) NSMutableArray *contents;

@property (nonatomic, strong) UIScrollView *scrollView;

@property (nonatomic, strong) UIPageControl *pageControl;

@property (nonatomic, strong) UIView *currentView;

@property (nonatomic, strong) UIView *otherView;

@property (nonatomic, assign) NSInteger currIndex;

@property (nonatomic, assign) NSInteger nextIndex;

@property (nonatomic, assign) CGSize pageImageSize;

@property (nonatomic, strong) NSTimer *timer;

@property (nonatomic, strong) NSOperationQueue *queue;
@end

static NSString *cache;


@implementation UICarouselView

- (instancetype)initWithFrame:(CGRect)frame {
    if (self = [super initWithFrame:frame]) {
        [self initSubView];
    }
    return self;
}

- (void)awakeFromNib {
    [super awakeFromNib];
    [self initSubView];
}

- (void)initSubView {
    self.autoCache = YES;
    [self addSubview:self.scrollView];
    [self addSubview:self.pageControl];
}

- (CGFloat)height {
    return self.scrollView.frame.size.height;
}

- (CGFloat)width {
    return self.scrollView.frame.size.width;
}

- (NSOperationQueue *)queue {
    if (!_queue) {
        _queue = [[NSOperationQueue alloc] init];
    }
    return _queue;
}

- (UIScrollView *)scrollView {
    if (!_scrollView) {
        _scrollView = [[UIScrollView alloc] init];
        _scrollView.scrollsToTop = NO;
        _scrollView.pagingEnabled = YES;
        _scrollView.bounces = NO;
        _scrollView.showsHorizontalScrollIndicator = NO;
        _scrollView.showsVerticalScrollIndicator = NO;
        _scrollView.delegate = self;
        [_scrollView addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(imageClick)]];
    }
    return _scrollView;
}

- (UIPageControl *)pageControl {
    if (!_pageControl) {
        _pageControl = [[UIPageControl alloc] init];
        _pageControl.userInteractionEnabled = NO;
    }
    return _pageControl;
}


#pragma mark- Setting
- (void)setContentMode:(UIViewContentMode)contentMode {
    _contentMode = contentMode;
    _currentView.contentMode = contentMode;
    _otherView.contentMode = contentMode;
}
- (void)setContentsArray:(NSArray *)contentsArray {
    if (!contentsArray || contentsArray.count <= 0) return;
    _contentsArray = contentsArray;
    _contents = [NSMutableArray array];
    for (int i = 0; i < contentsArray.count; i++) {
        id content = contentsArray[i];
        if ([content isKindOfClass:[UIImage class]]) {
            [_contents addObject:contentsArray[i]];
            [self registerCustomViewClass:UIImageView.class];
        } else if ([content isKindOfClass:[NSString class]]){
            [_contents addObject:content];
            [self registerCustomViewClass:UIImageView.class];
        } else {
            [_contents addObject:content];
        }
    }
    if (_currIndex >= _contents.count) _currIndex = _contents.count - 1;
    [self renderingLayerContent:_currentView index:_currIndex];
    _pageControl.numberOfPages = _contents.count;
    [self layoutSubviews];
}

- (void)setScrollViewContentSize {
    if (_contents.count > 1) {
        self.scrollView.contentSize = CGSizeMake(self.width * 5, 0);
        self.scrollView.contentOffset = CGPointMake(self.width * 2, 0);
        self.currentView.frame = CGRectMake(self.width * 2, 0, self.width, self.height);
        if (_changeMode == ChangeModeFade) {
            _currentView.frame = CGRectMake(0, 0, self.width, self.height);
            _otherView.frame = _currentView.frame;
            _otherView.alpha = 0;
            [self insertSubview:self.currentView atIndex:0];
            [self insertSubview:self.otherView atIndex:1];
        }
        _disabledTimer ? [self stopTimer] : [self startTimer];
    } else {
        self.scrollView.contentSize = CGSizeZero;
        self.scrollView.contentOffset = CGPointZero;
        self.currentView.frame = CGRectMake(0, 0, self.width, self.height);
        [self stopTimer];
    }
}

- (void)setPageImage:(UIImage *)image andCurrentPageImage:(UIImage *)currentImage {
    if (!image || !currentImage) return;
    self.pageImageSize = image.size;
    [self.pageControl setValue:currentImage forKey:@"_currentPageImage"];
    [self.pageControl setValue:image forKey:@"_pageImage"];
}

- (void)setPageColor:(UIColor *)color andCurrentPageColor:(UIColor *)currentColor {
    _pageControl.pageIndicatorTintColor = color;
    _pageControl.currentPageIndicatorTintColor = currentColor;
}

- (void)setPagePosition:(PageControlPosition)pagePosition {
    _pagePosition = pagePosition;
    _pageControl.hidden = (_pagePosition == PositionHide) || (_contentsArray.count == 1);
    if (_pageControl.hidden) return;
    
    CGSize size;
    if (!_pageImageSize.width) {//没有设置图片，系统原有样式
        size = [_pageControl sizeForNumberOfPages:_pageControl.numberOfPages];
        size.height = 8;
    } else {//设置图片了
        size = CGSizeMake(_pageImageSize.width * (_pageControl.numberOfPages * 2 - 1), _pageImageSize.height);
    }
    _pageControl.frame = CGRectMake(0, 0, size.width, size.height);
    
    CGFloat centerY = self.height - size.height * 0.5 - VERMARGIN;
    CGFloat pointY = self.height - size.height - VERMARGIN;
    if (_pagePosition == PositionDefault || _pagePosition == PositionBottomCenter)
        _pageControl.center = CGPointMake(self.width * 0.5, centerY);
    else if (_pagePosition == PositionTopCenter)
        _pageControl.center = CGPointMake(self.width * 0.5, size.height * 0.5 + VERMARGIN);
    else if (_pagePosition == PositionBottomLeft)
        _pageControl.frame = CGRectMake(HORMARGIN, pointY, size.width, size.height);
    else
        _pageControl.frame = CGRectMake(self.width - HORMARGIN - size.width, pointY, size.width, size.height);
    
    if (!CGPointEqualToPoint(_pageOffset, CGPointZero)) {
        self.pageOffset = _pageOffset;
    }
}

- (void)setPageOffset:(CGPoint)pageOffset {
    _pageOffset = pageOffset;
    CGRect frame = _pageControl.frame;
    frame.origin.x += pageOffset.x;
    frame.origin.y += pageOffset.y;
    _pageControl.frame = frame;
}

- (void)setTime:(NSTimeInterval)time {
    _time = time;
    _disabledTimer ? [self stopTimer] : [self startTimer];
}

- (void)setChangeMode:(ChangeMode)changeMode {
    _changeMode = changeMode;
    if (changeMode == ChangeModeFade) {
        _gifPlayMode = GifPlayModeAlways;
    }
}

- (void)setGifPlayMode:(GifPlayMode)gifPlayMode {
    if (_changeMode == ChangeModeFade) return;
    _gifPlayMode = gifPlayMode;
    if (gifPlayMode == GifPlayModeAlways) {
        [self gifAnimating:YES];
    } else if (gifPlayMode == GifPlayModeNever) {
        [self gifAnimating:NO];
    }
}

- (void)startTimer {
    if (_contents.count <= 1) return;
    if (self.timer) [self stopTimer];
    __weak typeof(self) weakSelf = self;
    self.timer = [NSTimer __timerWithTimeInterval:_time < 1? DEFAULTTIME: _time repeats:YES block:^(NSTimer * _Nonnull timer) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        [strongSelf nextPage];
    }];
    [[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}

- (void)stopTimer {
    [self.timer invalidate];
    self.timer = nil;
}

- (void)nextPage {
    if (_changeMode == ChangeModeFade) {
        _nextIndex = (_currIndex + 1) % _contents.count;
        [self renderingLayerContent:_otherView index:_nextIndex];
        @weakify(self);
        [UIView animateWithDuration:1.2 animations:^{
            @strongify(self);
            self.currentView.alpha = 0;
            self.otherView.alpha = 1;
            self.pageControl.currentPage = self.nextIndex;
        } completion:^(BOOL finished) {
            @strongify(self);
            [self changeToNext];
        }];
    } else {
        [self.scrollView setContentOffset:CGPointMake(self.width * 3, 0) animated:YES];
    }
}

- (void)layoutSubviews {
    [super layoutSubviews];
    _scrollView.contentInset = UIEdgeInsetsZero;
    _scrollView.frame = self.bounds;
    self.pagePosition = _pagePosition;
    [self setScrollViewContentSize];
}

- (void)imageClick {
    if (self.clickBlock) {
        self.clickBlock(self.currIndex);
    } else if ([_delegate respondsToSelector:@selector(carouselView:clickImageAtIndex:)]){
        [_delegate carouselView:self clickImageAtIndex:self.currIndex];
    }
}

- (void)changeCurrentPageWithOffset:(CGFloat)offsetX {
    if (offsetX < self.width * 1.5) {
        NSInteger index = self.currIndex - 1;
        if (index < 0) index = _contents.count - 1;
        _pageControl.currentPage = index;
    } else if (offsetX > self.width * 2.5){
        _pageControl.currentPage = (self.currIndex + 1) % _contents.count;
    } else {
        _pageControl.currentPage = self.currIndex;
    }
}

#pragma mark- UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
    if (CGSizeEqualToSize(CGSizeZero, scrollView.contentSize)) return;
    scrollView.contentOffset = CGPointMake(scrollView.contentOffset.x, 0);
    CGFloat offsetX = scrollView.contentOffset.x;
    if (_disabledCarousel) {
        if ((offsetX < self.width * 2 && _currIndex == 0)||(offsetX > self.width * 2 && _currIndex == _contents.count-1)) {
            scrollView.contentOffset =  CGPointMake(2 * self.width, 0);
            return;
        }
    }
    if (_gifPlayMode == GifPlayModePauseWhenScroll) {
        [self gifAnimating:(offsetX == self.width * 2)];
    }
    [self changeCurrentPageWithOffset:offsetX];
    if (offsetX < self.width * 2) {
        if (_changeMode == ChangeModeFade) {
            _currentView.alpha = offsetX / self.width - 1;
            _otherView.alpha = 2 - offsetX / self.width;
        } else {
            _otherView.frame = CGRectMake(self.width, 0, self.width, self.height);
        }
        _nextIndex = _currIndex - 1;
        if (_nextIndex < 0) _nextIndex = _contents.count - 1;
        [self renderingLayerContent:_otherView index:_nextIndex];
        if (offsetX <= self.width) [self changeToNext];
    } else if (offsetX > self.width * 2){
        if (_changeMode == ChangeModeFade) {
            _otherView.alpha = offsetX / self.width - 2;
            _currentView.alpha = 3 - offsetX / self.width;
        } else {
           _otherView.frame = CGRectMake(CGRectGetMaxX(_currentView.frame), 0, self.width, self.height);
        }
        _nextIndex = (_currIndex + 1) % _contents.count;
        [self renderingLayerContent:_otherView index:_nextIndex];
        if (offsetX >= self.width * 3) [self changeToNext];
    }
}

- (void)changeToNext {
    if (_changeMode == ChangeModeFade) {
        _currentView.alpha = 1;
        _otherView.alpha = 0;
    }
    [self renderingLayerContent:_currentView index:_nextIndex];
    self.scrollView.contentOffset = CGPointMake(self.width * 2, 0);
    [self.scrollView layoutSubviews];
    self.currIndex = self.nextIndex;
    self.pageControl.currentPage = self.currIndex;
}

- (void)scrollViewWillBeginDragging:(UIScrollView *)scrollView {
    [self stopTimer];
}

- (void)scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate{
    _disabledTimer ? [self stopTimer] : [self startTimer];
}

- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
    if (_changeMode == ChangeModeFade) return;
    CGPoint currPointInSelf = [_scrollView convertPoint:_currentView.frame.origin toView:self];
    if (currPointInSelf.x >= -self.width / 2 && currPointInSelf.x <= self.width / 2)
        [self.scrollView setContentOffset:CGPointMake(self.width * 2, 0) animated:YES];
    else [self changeToNext];
}

- (void)gifAnimating:(BOOL)b {
//    [self gifAnimating:b view:self.currentView];
//    [self gifAnimating:b view:self.otherView];
}

- (void)gifAnimating:(BOOL)isPlay view:(UIImageView *)imageV {
    if (isPlay) {
        CFTimeInterval pausedTime = [imageV.layer timeOffset];
        imageV.layer.speed = 1.0;
        imageV.layer.timeOffset = 0.0;
        imageV.layer.beginTime = 0.0;
        CFTimeInterval timeSincePause = [imageV.layer convertTime:CACurrentMediaTime() fromLayer:nil] -    pausedTime;
        imageV.layer.beginTime = timeSincePause;
    } else {
        CFTimeInterval pausedTime = [imageV.layer convertTime:CACurrentMediaTime() fromLayer:nil];
        imageV.layer.speed = 0.0;
        imageV.layer.timeOffset = pausedTime;
    }
}

static NSInteger previous = -10;
static __weak UIView *pView = nil;
- (void)renderingLayerContent:(UIView *)view index:(NSInteger)idx {
    if (previous == idx && pView == view) return;
    if ([view isKindOfClass:UIImageView.class]) {
        [self handleImageViewContent:(UIImageView *)view content:_contents[idx]];
    } else {
        [self handleCustomViewContent:view content:_contents[idx] index:idx];
    }
    previous = idx;
    pView = view;
}
- (void)handleImageViewContent:(UIImageView *)view content:(id)content {
    if ([content isKindOfClass:UIImage.class]) {
        [view qmui_removeAllSubviews];
        view.image = content;
    } else if ([content isKindOfClass:NSString.class]) {
        @weakify(self);
        NSString *string = [content tx_stringByEncodingURL].length>0 ? [content tx_stringByEncodingURL] : nil;
        [view az_setImageWithURL:[NSURL URLWithString:string] placeholderImage:self.placeholderImage showLoadingIndicator:self.showLoadingIndicator animation:YES completion:^(UIImage * _Nullable image) {
            @strongify(self);
            if ([self.contents containsObject:content] && image!=nil) {
                [self.contents replaceObjectAtIndex:[self.contents indexOfObject:content] withObject:image];
            }
        }];
    }
}
- (void)handleCustomViewContent:(UIView *)view content:(id)content index:(NSInteger)idx {
    if (self.renderer) {
        self.renderer(view, content, idx);
        return;
    }
}
- (void)registerCustomViewClass:(Class)cls {
    [_currentView removeFromSuperview];
    [_otherView removeFromSuperview];
    NSParameterAssert([cls isEqual:UIImageView.class] || [cls conformsToProtocol:@protocol(UICarouselViewCustomViewDelegate)]);
    _currentView = [[cls alloc] init];
    _currentView.clipsToBounds = YES;
    [_scrollView addSubview:_currentView];
    _otherView = [[cls alloc] init];
    _otherView.clipsToBounds = YES;
    [_scrollView addSubview:_otherView];
}
@end
